#include <numeric>
#include <algorithm>
#include <nice2d/application.h>
#include <nice2d/background.h>
#include <nice2d/widgets/desktop.h>
#include <nice2d/widgets/texttable.h>
#include <nice2d/widgets/label.h>

IMPLEMENT_OBJECT(TextTableStyle, LayoutStyle)

TextTableStyle::TextTableStyle()
{
    normalBackground = Background::create(sf::Color(136, 128, 128, 180));
    hoverBackground = normalBackground;
    labelStyle = std::make_shared<LabelStyle>();
    labelStyle->hoverBackground = labelStyle->normalBackground;
}

TextTableStyle::~TextTableStyle()
{
}

IMPLEMENT_OBJECT(TextTable, Layout)

class TextTableInfo : public LayoutInfo
{
public:
    uint32_t row;
    uint32_t col;

    TextTableInfo(uint32_t thisRow, uint32_t thisCol):
        row(thisRow),
        col(thisCol)
    {
    }
};

class TextTableData
{
public:
    TextTable *table = nullptr;
    std::vector<std::shared_ptr<Label>> labels;
    std::vector<float> colWidths;
    float rowHeight = 32.0f;

    bool addToLayout = false;

    void adjustTexts();
    void adjustText(std::shared_ptr<Label> label, std::shared_ptr<TextTableInfo> info);
    sf::Vector2f getPosition(int row, int col);
};

TextTable::TextTable(uint32_t rowcount, uint32_t colcount):
    data(new TextTableData)
{
    data->table = this;
    data->labels.resize(rowcount * colcount);
    std::generate(data->labels.begin(), data->labels.end(), []()->auto{return std::make_shared<Label>();});

    data->colWidths.resize(colcount);
    std::fill(data->colWidths.begin(), data->colWidths.end(), 72.0f);

    setResizeRule(ControlResizeRule_Fill, ControlResizeRule_Fill);

    auto desktop = GET_APPLICATION_COMPONENT(Desktop);
    auto style = desktop->getControlStyle("TextTable");
    setControlStyle(style);

    auto labelStyle = std::dynamic_pointer_cast<TextTableStyle>(style);
    auto itr = data->labels.begin();
    while (itr != data->labels.end()) {
        (*itr)->setControlStyle(labelStyle->labelStyle);
        (*itr)->setAligment(HMode_Center);
        itr ++;
    }
}

TextTable::~TextTable()
{
}

void TextTable::setText(const sf::String &text, uint32_t row, uint32_t col, const sf::Color &color)
{
    if (col >= data->colWidths.size())
        return;

    if (row >= data->labels.size() / data->colWidths.size())
        return;

    std::shared_ptr<Label> label = data->labels[row * data->colWidths.size() + col];
    label->setText(text);
    label->setTextColor(color);
}

void TextTable::setColWidth(uint32_t col, float width)
{
    if (data->colWidths.size() > col) {
        data->colWidths[col] = width;
        adjust();
    }
}

void TextTable::setRowHeight(float height)
{
    if (data->rowHeight != height) {
        data->rowHeight = height;
        adjust();
    }
}

void TextTable::onStyleChanged()
{
    Control::onStyleChanged();

    auto style = getTemplateStyle<TextTableStyle>();

    auto itr = data->labels.begin();
    while (itr != data->labels.end()) {
        (*itr)->setControlStyle(style->labelStyle);
        itr ++;
    }
}

void TextTable::adjust()
{
    if (!data->addToLayout) {
        data->addToLayout = true;

        uint32_t count = data->labels.size();
        for (uint32_t i = 0; i < count; i++) {
            auto label = data->labels[i];
            int row = i / data->colWidths.size();
            int col = i % data->colWidths.size();
            auto info = std::make_shared<TextTableInfo>(row, col);
            addWidget(label, info);
        }
    }

    auto width = std::accumulate(data->colWidths.begin(), data->colWidths.end(), 0.0f);
    width += 2.0f * getMargin();
    width += (data->colWidths.size() - 1) * getSpacing();

    int rowCount = data->labels.size() / data->colWidths.size();

    auto height = data->rowHeight * rowCount;
    height += 2.0f * getMargin();
    height += (rowCount - 1) * getSpacing();

    setSize(width, height);

    data->adjustTexts();
}

void TextTableData::adjustTexts()
{
    auto count = table->getWidgetCount();
    if (count == 0)
        return;

    for (uint32_t i = 0; i < count; i++) {
        auto item = table->getWidgetByIndex(i);
        auto widget = item.first;

        auto info = std::dynamic_pointer_cast<TextTableInfo>(item.second);
        widget->setSize(colWidths[info->col], rowHeight);
        adjustText(widget->as<Label>(), info);
    }
}

void TextTableData::adjustText(std::shared_ptr<Label> label, std::shared_ptr<TextTableInfo> info)
{
    auto position = getPosition(info->row, info->col);
    label->setPosition(position);
}

sf::Vector2f TextTableData::getPosition(int row, int col)
{
    sf::Vector2f pos(table->getMargin(), table->getMargin());
    pos.x += std::accumulate(colWidths.begin(), colWidths.begin() + col, 0.0f);
    if (col > 0)
        pos.x += col * table->getSpacing();
    if (row > 0)
        pos.y += row * (rowHeight + table->getSpacing());
    return pos;
}
